package com.liuyjy.aop.encryptDecrypt;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.liuyjy.base.common.enums.EncryptDecryptTypeEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;

/**
 * @Author liuyjy
 * @Date 2025/2/21 12:18
 * @Description: aop
 **/
@Aspect
@Component
public class CryptoAspect {
    @Autowired
    private CryptoProcessor cryptoProcessor;

    @Pointcut("@within(com.liuyjy.aop.encryptDecrypt.EncryptDecrypt) || " +
            "@annotation(com.liuyjy.aop.encryptDecrypt.EncryptDecrypt)")
    public void encryptDecryptPointcut() {
    }



    /**
     * 环绕通知，用于处理加密和解密逻辑
     * 该方法拦截标注了@EncryptDecrypt注解的方法执行前后，以处理加密或解密逻辑
     *
     * @param joinPoint 切入点对象，提供了关于目标方法的信息
     * @return 目标方法执行后的结果
     * @throws Throwable 目标方法执行过程中可能抛出的异常
     */
    @Around("encryptDecryptPointcut()")
    public Object handleCrypto(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取目标方法的反射对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        // 优先级：方法注解 > 类注解 > 默认关闭
        // 检查方法上是否有EncryptDecrypt注解
        EncryptDecrypt methodAnnotation = method.getAnnotation(EncryptDecrypt.class);
        if (methodAnnotation != null) {
            // 如果方法上有注解，则根据该注解处理加密解密逻辑
            return handleWithAnnotation(methodAnnotation, joinPoint, method, args);
        }

        // 检查类上是否有EncryptDecrypt注解
        EncryptDecrypt classAnnotation = method.getDeclaringClass().getAnnotation(EncryptDecrypt.class);
        if (classAnnotation != null) {
            // 如果类上有注解，则根据该注解处理加密解密逻辑
            return handleWithAnnotation(classAnnotation, joinPoint, method, args);
        }

        // 如果没有注解，默认不处理加密解密
        // 继续执行目标方法，不进行加密解密处理
        return joinPoint.proceed(args);
    }

    private Object handleWithAnnotation(EncryptDecrypt annotation, ProceedingJoinPoint joinPoint,
                                        Method method, Object[] args) throws Throwable {

        EncryptDecryptTypeEnum typeEnum = annotation.encryptDecryptType();
        //本次处理时解密还是加密
        boolean encryptType=false;
        // 加解密类型
        if(typeEnum.equals(EncryptDecryptTypeEnum.INPUT_ENCRYPT_OUTPUT_DECRYPT)){
            // 输入加密，输出解密
            encryptType=true;
        }

        // 解密目标方法的参数，确保参数在传递过程中保持明文状态
        Object[] decryptedArgs = decryptArgs(args, method,encryptType);

        // 执行目标方法，并传入解密后的参数
        Object result = joinPoint.proceed(decryptedArgs);

        // 加密目标方法的返回结果，确保返回数据在传输过程中安全
        return encryptResult(result, method, !encryptType);
    }

    /**
     * 解密方法参数
     * 此方法用于处理传入方法的参数数组，对于标注了Decrypt注解的参数，将其进行解密处理
     *
     * @param args   方法的参数数组，可能包含需要解密的参数
     * @param method 正在执行的方法，用于获取参数信息
     * @param encryptType 本次处理时解密还是加密
     * @return 返回处理后的参数数组，其中标注了Decrypt注解的参数已被解密
     */
    private Object[] decryptArgs(Object[] args, Method method,boolean encryptType) {
        // 获取方法的参数信息
        Parameter[] parameters = method.getParameters();
        // 遍历参数数组，检查每个参数是否需要解密
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];

            // 获取参数的类型信息，以便于解密处理时正确处理类型
            ResolvableType resolvableType = ResolvableType.forMethodParameter(method, i);

            //为否时只针对对象内部的值加密，例如 List<Long> 集合内部的值组个加密
            boolean enableALL = false;
           // JavaType javaType = getReturnTypeAsJavaType(method, i);
            // 如果参数标注了Decrypt注解，则对该参数进行解密处理
            if (!encryptType && parameter.isAnnotationPresent(Decrypt.class)) {

                // 使用cryptoProcessor对参数进行解密处理
                args[i] = cryptoProcessor.processObject(args[i], resolvableType, false,encryptType,enableALL);

            }else if (encryptType && parameter.isAnnotationPresent(Encrypt.class)){
                Encrypt encrypt = parameter.getAnnotation(Encrypt.class);
                enableALL = encrypt.enableALL();
                args[i] = cryptoProcessor.processObject(args[i], resolvableType, true,encryptType,enableALL);

            }else {
                // 如果参数没有标注Decrypt注解，则使用cryptoProcessor进行加密处理
                args[i] = cryptoProcessor.processObject(args[i], resolvableType, null,encryptType,enableALL);
            }

        }
        // 返回处理后的参数数组
        return args;
    }

    /**
     * 对方法返回结果进行加密处理
     * 此方法旨在为标记了Encrypt注解的方法自动加密返回结果
     *
     * @param result 待加密的原始返回结果
     * @param method 调用的方法，用于检查是否需要加密以及获取返回类型信息
     * @param encryptType 本次处理时解密还是加密
     * @return 加密后的结果，如果方法不需要加密则返回原始结果
     */
    private Object encryptResult(Object result, Method method,boolean encryptType){

        // 获取方法的返回类型，以便于后续处理
         ResolvableType returnType = ResolvableType.forMethodReturnType(method);

        //JavaType javaType = getReturnTypeAsJavaType(method);
        boolean enableALL = false;
        // 检查方法是否被Encrypt注解标记
        if (method.isAnnotationPresent(Encrypt.class)) {
            Encrypt encrypt = method.getAnnotation(Encrypt.class);
             enableALL = encrypt.enableALL();
            // 使用cryptoProcessor对结果进行加密处理
            return cryptoProcessor.processObject(result, returnType, true,encryptType,enableALL);
        }
        if (method.isAnnotationPresent(Decrypt.class)) {
            // 使用cryptoProcessor对结果进行加密处理
            return cryptoProcessor.processObject(result, returnType, false,encryptType,enableALL);
        }

        return cryptoProcessor.processObject(result, returnType, null,encryptType,enableALL);
    }

    /**
     * 将 Method 的返回类型转换为 JavaType
     *
     * @param method 需要转换的 Method 对象
     * @return 转换后的 JavaType 对象
     */
    public static JavaType getReturnTypeAsJavaType(Method method, int index) {
        TypeFactory typeFactory = TypeFactory.defaultInstance();
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        return typeFactory.constructType(genericParameterTypes[index]);
    }

    /**
     * 将 Method 的返回类型转换为 JavaType
     *
     * @param method 需要转换的 Method 对象
     * @return 转换后的 JavaType 对象
     */
    public static JavaType getReturnTypeAsJavaType(Method method) {
        return TypeFactory.defaultInstance().constructType(method.getGenericReturnType());
    }
}